package com.cv.framework.security.core.filter;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.cv.framework.common.pojo.Result;
import com.cv.framework.common.utils.json.JsonUtil;
import com.cv.framework.common.utils.servlet.ServletUtil;
import com.cv.framework.security.config.SecurityProperties;
import com.cv.framework.security.core.utils.JwtUtil;
import com.cv.framework.security.core.utils.SecurityFrameworkUtil;
import com.cv.framework.web.core.handler.GlobalExceptionHandler;
import com.cv.system.api.token.form.LoginUser;
import lombok.RequiredArgsConstructor;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Token 过滤器，验证 token 的有效性
 * 验证通过后，获得 {@link LoginUser} 信息，并加入到 Spring Security 上下文
 *
 * @author Charles_XDXD
 */
@RequiredArgsConstructor
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private final SecurityProperties securityProperties;

    private final GlobalExceptionHandler globalExceptionHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        // 基于 header[login-user] 获取用户。如：网关服务等......
        LoginUser loginUser = buildLoginUserByHeader(request);

        // 基于 Token 获得用户
        String token = SecurityFrameworkUtil.obtainAuthorization
                (
                        request,
                        securityProperties.getTokenHeader(),
                        securityProperties.getTokenParameter()
                );

        if (StrUtil.isNotEmpty(token) && JwtUtil.validateToken(token)) {
            try {
                if (ObjUtil.isEmpty(loginUser)) {
                    loginUser = JwtUtil.parseToken(token);
                }

                if (JwtUtil.isTokenExpiringSoon(token)) {
                    String refreshToken = JwtUtil.refreshToken(token);

                    if (StrUtil.isNotEmpty(refreshToken)) {
                        response.setHeader(securityProperties.getTokenHeader(),
                                SecurityFrameworkUtil.AUTHORIZATION_BEARER + StrUtil.SPACE + refreshToken);
                        
                        // 一定要添加这两句，否则前端获取不到该响应头
                        response.setHeader("Access-Control-Expose-Headers", securityProperties.getTokenHeader());
                        response.setHeader("Access-Control-Expose-Header", securityProperties.getTokenHeader());
                    }
                }
            } catch (Throwable ex) {
                Result<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
                ServletUtil.writeJSON(response, result);
                return;
            }
        }

        // 设置当前用户
        if (loginUser != null) {
            SecurityFrameworkUtil.setLoginUser(loginUser, request);
        }

        chain.doFilter(request, response);
    }

    private LoginUser buildLoginUserByHeader(HttpServletRequest request) {
        String loginUserStr = request.getHeader(SecurityFrameworkUtil.LOGIN_USER_HEADER);
        return StrUtil.isNotEmpty(loginUserStr) ? JsonUtil.parseObject(loginUserStr, LoginUser.class) : null;
    }

}

